# A part of NonVisual Desktop Access (NVDA)
# Copyright (C) 2025 NV Access Limited
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.

"""Tools for interacting with Windows' MMDevice API."""

from collections.abc import Generator
from typing import NamedTuple, cast

import config
from pycaw.constants import DEVICE_STATE, EDataFlow
from pycaw.utils import AudioUtilities


class AudioOutputDevice(NamedTuple):
	"""An MMDevice audio render endpoint."""

	id: str
	"""The unique identifier of the audio endpoint."""

	friendlyName: str
	"""The user-friendly name of the audio endpoint."""


def getOutputDevices(
	*,
	includeDefault: bool = False,
	stateMask: DEVICE_STATE = DEVICE_STATE.ACTIVE,
) -> Generator[AudioOutputDevice]:
	"""Generator, yielding device ID and device Name.
	.. note:: Depending on number of devices being fetched, this may take some time (~3ms)

	:param includeDefault: Whether to include a value representing the system default output device in the generator, defaults to False.
		.. note:: The ID of this device is **not** a valid mmdevice endpoint ID string, and is for internal use only.
			The friendly name is **not** generated by the operating system, and it is highly unlikely that it will match any real output device.
	:param state: What device states to include in the resultant generator, defaults to DEVICE_STATE.ACTIVE.
	:return: Generator of :class:`_AudioOutputDevices` containing all enabled and present audio output devices on the system.
	"""
	if includeDefault:
		yield AudioOutputDevice(
			id=cast(str, config.conf.getConfigValidation(("audio", "outputDevice")).default),
			# Translators: Value to show when choosing to use the default audio output device.
			friendlyName=_("Default output device"),
		)
	endpointCollection = AudioUtilities.GetDeviceEnumerator().EnumAudioEndpoints(
		EDataFlow.eRender.value,
		stateMask.value,
	)
	for i in range(endpointCollection.GetCount()):
		device = AudioUtilities.CreateDevice(endpointCollection.Item(i))
		# This should never be None, but just to be sure
		if device is not None:
			yield AudioOutputDevice(device.id, device.FriendlyName)
		else:
			continue
